<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Program Listing</title>
    <link rel="stylesheet" href="gettingStarted.css" type="text/css" />
    <meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
    <link rel="start" href="index.html" title="Getting Started with Replicated Berkeley DB Applications" />
    <link rel="up" href="writeforwarding.html" title="Chapter 5. Configuring for Write Forwarding" />
    <link rel="prev" href="writeforwarding.html" title="Chapter 5. Configuring for Write Forwarding" />
    <link rel="next" href="addfeatures.html" title="Chapter 6. Additional Features" />
  </head>
  <body>
    <div xmlns="" class="navheader">
      <div class="libver">
        <p>Library Version 12.1.6.2</p>
      </div>
      <table width="100%" summary="Navigation header">
        <tr>
          <th colspan="3" align="center">Program Listing</th>
        </tr>
        <tr>
          <td width="20%" align="left"><a accesskey="p" href="writeforwarding.html">Prev</a> </td>
          <th width="60%" align="center">Chapter 5. Configuring for Write Forwarding</th>
          <td width="20%" align="right"> <a accesskey="n" href="addfeatures.html">Next</a></td>
        </tr>
      </table>
      <hr />
    </div>
    <div class="sect1" lang="en" xml:lang="en">
      <div class="titlepage">
        <div>
          <div>
            <h2 class="title" style="clear: both"><a id="wrfor_programlisting"></a>Program Listing</h2>
          </div>
        </div>
      </div>
      <div class="toc">
        <dl>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#repwrforconfiginfo_cxx">
                            
                            <span>Class: RepWrforConfig</span>
                    </a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#simplewrforusage_java">Class: RepQuoteWrforExampleGSG</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrformain_java">Method: RepQuoteWrforExampleGSG.main()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrfor_init_java">Method: RepQuoteWrforExampleGSG.init()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#doloop_wrforjava">Method: RepQuoteWrforExampleGSG.doloop()</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="wrfor_programlisting.html#wrfor_printstocks_c">
                            
                            
                            <span>Method: RepQuoteWrforExampleGSG.printStocks()</span>    </a>
            </span>
          </dt>
        </dl>
      </div>
      <p> 
            Our example program builds from the simple transactional
            application you created in the <a class="xref" href="txnapp.html" title="Chapter 2. Transactional Application">Transactional Application</a> 
            chapter and configures write forwarding. The application is 
            network-aware, so you can specify things like host names and 
            ports from the command line. This program has additional error
            handling for replication errors. 
        </p>
      <p> 
            When using replication with write forwarding, there are several benefits for your application code: 
        </p>
      <div class="itemizedlist">
        <ul type="disc">
          <li>
            <p> 
                    You do not need to create an event handler to detect
                    changes of the master.
                </p>
          </li>
          <li>
            <p> 
                    You do not need to use app_data to track whether the
                    current site is master. 
                </p>
          </li>
          <li>
            <p>
                    You do not need to provide an error for put
                    operations on the client. 
                </p>
          </li>
        </ul>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="repwrforconfiginfo_cxx"></a>
                            
                            <span>Class: RepWrforConfig</span>
                    </h3>
            </div>
          </div>
        </div>
        <p>
                            Before we begin, we present a 
                            class that we will use to maintain useful
                            information for us. 
                    </p>
        <p>
                            The class that we create is called
                            
                            <code class="classname">RepWrforConfig</code>.
                            <span>
                            First, we provide some declarations and
                            definitions that are needed later in
                            our example. One is the size of our cache,
                            which we keep deliberately small for this example, and the other
                            is the name of our database. Also, you can define a sleep
                            time, which sets the time that a site waits before it retries
                            synchronizing with the master. We also provide a global variable that
                            is the name of our program; this is used for error reporting
                            later on. 
                            </span>
                    </p>
        <pre class="programlisting">package db.repquote_gsg;

public class RepWrforConfig
{
    // Constant values used in the RepQuote application.
    public static final String progname = "RepQuoteWrforExampleGSG";
    public static final int CACHESIZE = 10 * 1024 * 1024;
    public static final int SLEEPTIME = 5000;

    // Member variables containing configuration information.
    // String specifying the home directory for rep files.
    public String home;
    // Stores an optional set of "other" hosts.
    private Vector otherHosts;
    // Priority within the replication group.
    public ReplicationManagerStartPolicy startPolicy;
    // The host address to listen to.
    private ReplicationManagerSiteConfig thisHost;

    // Member variables used internally.
    private int currOtherHost;
    private boolean gotListenAddress;

    public RepWrforConfig()
    {
        startPolicy = ReplicationManagerStartPolicy.REP_ELECTION;
        home = "TESTDIR";
        gotListenAddress = false;
        currOtherHost = 0;
        thisHost = new ReplicationManagerSiteConfig();
        otherHosts = new Vector();
    }

    public java.io.File getHome()
    {
        return new java.io.File(home);
    }

}  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="simplewrforusage_java"></a>Class: RepQuoteWrforExampleGSG</h3>
            </div>
          </div>
        </div>
        <p>
                            Our example will 
                            consist of a class,
                            <code class="classname">RepQuoteWrforExampleGSG</code>, that performs
                            all our work for us. 
                    </p>
        <p>
                            First, we provide the package declaration and
                            then a few import statements that the class
                            needs.
                    </p>
        <pre class="programlisting">package db.repquote_gsg;

import java.io.FileNotFoundException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.Thread;
import java.lang.InterruptedException;

import com.sleepycat.db.Cursor;
import com.sleepycat.db.Database;
import com.sleepycat.db.DatabaseConfig;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.DeadlockException;
import com.sleepycat.db.DatabaseType;
import com.sleepycat.db.Environment;
import com.sleepycat.db.EnvironmentConfig;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.OperationStatus;
import com.sleepycat.db.ReplicationConfig;
import com.sleepycat.db.ReplicationHandleDeadException;
import com.sleepycat.db.ReplicationHostAddress;
import com.sleepycat.db.ReplicationManagerSiteConfig;
import com.sleepycat.db.ReplicationManagerAckPolicy;

import db.repquote_gsg.RepWrforConfig;

public class RepQuoteWrforExampleGSG
{
    private RepWrforConfig repConfig;
    private Environment dbenv; </pre>
        <p>
        Next, we provide our class constructor. This simply initializes our
        class data members.
</p>
        <pre class="programlisting">    public RepQuoteWrforExampleGSG() 
         throws DatabaseException
    {
         repConfig = null;
         dbenv = null;
    }  </pre>
        <p>
        And then we provide our <code class="methodname">usage()</code> method:
</p>
        <pre class="programlisting">    public static void usage()
    {
        System.err.println("usage: " + RepWrforConfig.progname);
        System.err.println("-h home -l|-L host:port " +
            "[-r host:port]");

        System.err.println("\t -h home directory (required)\n" +
             "\t -l host:port (required unless -L is specified;" +
             " l stands for local)\n" + 
             "\t -L host:port (optional, L means group creator)\n" +
             "\t -r host:port (optional; r stands for remote; any " +
             "number of these\n" +
             "\t    may be specified)\n");

        System.exit(1);
    }  </pre>
        <p> 
                where: 
            </p>
        <div class="itemizedlist">
          <ul type="disc">
            <li>
              <p>
                        <code class="literal">-h</code>
                    </p>
              <p> 
                        Identifies the environment home directory. You must
                        specify this option. 
                    </p>
            </li>
            <li>
              <p>
                        <code class="literal">-l</code>
                    </p>
              <p>
                        Identifies the host and port used by this site. You
                        must specify this option unless <code class="literal">-L</code> is
                        specified.
                    </p>
            </li>
            <li>
              <p>
                        <code class="literal">-L</code>
                    </p>
              <p> 
                        Identifies the local site as group creator. You must
                        specify this option unless <code class="literal">-l</code> is
                        specified.
                    </p>
            </li>
            <li>
              <p>
                        <code class="literal">-r</code>
                    </p>
              <p>
                        Optionally identifies another site participating in
                        this replication group.
                    </p>
            </li>
          </ul>
        </div>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrformain_java"></a>Method: RepQuoteWrforExampleGSG.main()</h3>
            </div>
          </div>
        </div>
        <p>
                            Having implemented our
                            <code class="methodname">usage()</code>
                            method, we can jump directly into our
                            <code class="methodname">main()</code>
                            method. This method begins by instantiating a
                            <code class="classname">RepWrforConfig</code> object, and
                            then collecting the command line arguments so
                            that it can populate the object with the
                            appropriate data:
                    </p>
        <pre class="programlisting">    public static void main(String[] argv)
    throws Exception
{
    RepWrforConfig config = new RepWrforConfig();
    boolean isCreator = false;
    int tmpPort = 0;
    // Extract the command line parameters
    for (int i = 0; i &lt; argv.length; i++)
    {
        if (argv[i].compareTo("-h") == 0) {
            // home is a string arg.
            i++;
            config.home = argv[i];
        } else if (argv[i].compareTo("-l") == 0 ||
          argv[i].compareTo("-L") == 0) {
            if (i == argv.length - 1)
                usage();
            if (argv[i].compareTo("-L") == 0)
                isCreator = true;
            // "local" should be host:port.
            i++;
            // Look for index of the last colon in the argv[i] string.
            int sep = argv[i].lastIndexOf(':');
            if (sep == -1 || sep == 0) {
                System.err.println(
                    "Invalid local host specification host:port needed.");
                usage();
            }
            try {
                tmpPort = Integer.parseInt(argv[i].substring(sep + 1));
            } catch (NumberFormatException nfe) {
                System.err.println("Invalid local host specification, " +
                    "could not parse port number.");
                usage();
            }
            config.setThisHost(argv[i].substring(0, sep), 
                                                     tmpPort, isCreator);
        } else if (argv[i].compareTo("-r") == 0) {
            i++;
            // Look for index of the last colon in the argv[i] string.
            int sep = argv[i].lastIndexOf(':');
            if (sep == -1 || sep == 0) {
                System.err.println(
                    "Invalid remote host specification host:port needed.");
                usage();
            }
            try {
                tmpPort = Integer.parseInt(argv[i].substring(sep + 1));
            } catch (NumberFormatException nfe) {
                System.err.println("Invalid remote host specification, " +
                    "could not parse port number.");
                usage();
            }
            config.addOtherHost(argv[i].substring(0, sep), tmpPort);
        } else {
            System.err.println("Unrecognized option: " + argv[i]);
            usage();
        }

    }  </pre>
        <p>
        And then perform a little sanity checking on the command line
        input:
</p>
        <pre class="programlisting">    // Error check command line.
    if ((!config.gotListenAddress()) || config.home.length() == 0)
        usage();  </pre>
        <p>
            Now we perform the class' work. To begin, we initialize the
            object. The <code class="methodname">init()</code> method actually
            opens our environment for us (shown in the next section).
    </p>
        <pre class="programlisting">    RepQuoteWrforExampleGSG runner = null;
    try {
        runner = new RepQuoteWrforExampleGSG();
        runner.init(config);  </pre>
        <p>
            And then we call our <code class="methodname">doloop()</code>
            method. This method is where we perform all our database
            activity.
    </p>
        <pre class="programlisting">        runner.doloop();  </pre>
        <p>
            And then, finally terminate the application (which closes our
            environment handle) and end the method. Note, 
            again, that in a traditional transactional application all 
            databases would be closed here. In our replicated application,
            the database will usually be closed in the <code class="function">doloop()</code>
            function, but we also conditionally close the database here to handle
            some error cases.
    </p>
        <pre class="programlisting">        runner.terminate();
    } catch (DatabaseException dbe) {
        System.err.println("Caught an exception during " +
            "initialization or processing: " + dbe.toString());
        if (runner != null)
            runner.terminate();
    }
        System.exit(0);
} // end main  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrfor_init_java"></a>Method: RepQuoteWrforExampleGSG.init()</h3>
            </div>
          </div>
        </div>
        <p>
                        The <code class="methodname">RepQuoteWrforExampleGSG.init()</code>
                        method is used to open our environment handle.              
                        You can now configure and start Replication Manager with
                        write forwarding. To configure write forwarding, use
                        <code class="function">setReplicationConfig</code> with the
                        <code class="literal">ReplicationConfig.FORWARD_WRITES</code> option:
                </p>
        <pre class="programlisting">    public int init(RepWrforConfig config)
        throws DatabaseException
    {
        int ret = 0;
        repConfig = config;
        EnvironmentConfig envConfig = new EnvironmentConfig();
        envConfig.setErrorStream(System.err);
        envConfig.setErrorPrefix(RepWrforConfig.progname);

        envConfig.addReplicationManagerSite(repConfig.getThisHost());
        for (ReplicationHostAddress host = repConfig.getFirstOtherHost();
          host != null; host = repConfig.getNextOtherHost()){

            ReplicationManagerSiteConfig repmgrRemoteSiteConfig =
                new ReplicationManagerSiteConfig(host.host, host.port);
            repmgrRemoteSiteConfig.setBootstrapHelper(true);
            envConfig.addReplicationManagerSite(
                repmgrRemoteSiteConfig);
        }

        envConfig.setCacheSize(RepWrforConfig.CACHESIZE);
        envConfig.setTxnNoSync(true);

        envConfig.setAllowCreate(true);
        envConfig.setRunRecovery(true);
        envConfig.setThreaded(true);
        envConfig.setInitializeReplication(true);
        envConfig.setInitializeLocking(true);
        envConfig.setInitializeLogging(true);
        envConfig.setInitializeCache(true);
        envConfig.setTransactional(true);
        try {
            dbenv = new Environment(repConfig.getHome(), envConfig);
        } catch(FileNotFoundException e) {
            System.err.println("FileNotFound exception: " + e.toString());
            System.err.println(
                "Ensure that the environment directory is pre-created.");
            ret = 1;
        }

        // Configure Replication Manager write forwarding.
        dbenv.setReplicationConfig(ReplicationConfig.FORWARD_WRITES, true);

        // Start Replication Manager.
        dbenv.replicationManagerStart(3, repConfig.startPolicy);
        return ret;
    }  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="doloop_wrforjava"></a>Method: RepQuoteWrforExampleGSG.doloop()</h3>
            </div>
          </div>
        </div>
        <p>
                        We now implement our application's
                        primary data processing method. This
                        method provides a command prompt at which the
                        user can enter a stock ticker value and a price for
                        that value. This information is then entered to the
                        database.
                    </p>
        <p>
                            To display the database, simply enter
                            <code class="literal">return</code> at the prompt.
                    </p>
        <p>
                        To begin, we declare a database pointer:
                    </p>
        <pre class="programlisting">    public int doloop()
        throws DatabaseException
    {
        Database db = null;  </pre>
        <p>
                    Next, we begin the loop and we immediately open our
                    database if it has not already been opened. 
                </p>
        <p> 
                    If <code class="literal">-L</code> is set, it specifies the need to
                    create the database for the initial group creator startup. The
                    database will be replicated to the other sites when they first
                    start up. The database will already exist on each site for
                    subsequent startups.
                </p>
        <p>
                    Note that there is some logic for a site to retry in case it
                    needs time to synchronize with the master using
                    <code class="literal">SLEEPTIME</code>.
                </p>
        <pre class="programlisting">        for (;;)
        {
            if (db == null) {
                DatabaseConfig dbconf = new DatabaseConfig();
                dbconf.setType(DatabaseType.BTREE);
                if (repConfig.getThisHost().getGroupCreator()) {
                    dbconf.setAllowCreate(true);
                }
                dbconf.setTransactional(true);

                try {
                    db = dbenv.openDatabase
                        (null, RepWrforConfig.progname, null, dbconf);
                } catch (java.io.FileNotFoundException e) {
                    System.err.println("No stock database available yet.");
                    if (db != null) {
                        db.close(true);
                        db = null;
                    }
                    try {
                        Thread.sleep(RepWrforConfig.SLEEPTIME);
                    } catch (InterruptedException ie) {}
                    continue;
                }
            }  </pre>
        <p>
            Now we implement our command prompt.
            If the user enters the keywords <code class="literal">exit</code>
            or <code class="literal">quit</code>, the loop is exited and the
            application ends. If the user enters nothing and instead simply
            presses <code class="literal">return</code>, the entire contents of the
            database is displayed. We use our
            <code class="methodname">printStocks()</code> method to display the
            database. (That implementation is shown next in this chapter.)
        </p>
        <p>
            We also now check for a dead replication handle, which can occur
            in rare cases when a new master causes a previously committed 
            transaction to be rolled back. In such cases, all database handles
            must be closed and opened again.
        </p>
        <p> 
            Remember that very little error checking is performed on the
            data entered at this prompt. If the user fails to enter at least
            one space in the value string, a simple help message is printed
            and the prompt is returned to the user.
        </p>
        <pre class="programlisting">            BufferedReader stdin =
                new BufferedReader(new InputStreamReader(System.in));

            // Listen for input, and add it to the database.
            System.out.print("QUOTESERVER");
            System.out.print("&gt; ");
            System.out.flush();
            String nextline = null;
            try {
                nextline = stdin.readLine();
            } catch (IOException ioe) {
                System.err.println("Unable to get data from stdin");
                break;
            }
            String[] words = nextline.split("\\s");

            // A blank line causes the DB to be dumped to stdout.
            if (words.length == 0 || 
                (words.length == 1 &amp;&amp; words[0].length() == 0)) {
                try {
                    printStocks(db);
                } catch (DeadlockException de) {
                    continue;
                // Dead replication handles are caused by an election
                // resulting in a previously committing read becoming
                // invalid.  Close the db handle and reopen.
                } catch (ReplicationHandleDeadException rhde) {
                    db.close(true); // close no sync.
                    db = null;
                    continue;
                } catch (DatabaseException e) {
                    System.err.println("Got db exception reading
                        replication" + "DB: " + e.toString());
                    break;
                }
                continue;
            }

            if (words.length == 1 &amp;&amp;
                (words[0].compareToIgnoreCase("quit") == 0 ||
                words[0].compareToIgnoreCase("exit") == 0)) {
                break;
            } else if (words.length != 2) {
                System.err.println("Format: TICKER VALUE");
                continue;
            }  </pre>
        <p>
                Now we assign data to the <code class="classname">DatabaseEntry</code> 
                classes that we will use to write the new information to the database.
            </p>
        <pre class="programlisting">            DatabaseEntry key =
                    new DatabaseEntry(words[0].getBytes());
            DatabaseEntry data =
                    new DatabaseEntry(words[1].getBytes());  </pre>
        <p>
                Having done that, we can write the new information to the
                database. Here, the reason we do not need an explicit commit 
                on this put operation is that it uses the implicit NULL txnid, 
                so each one is automatically committed. 
            </p>
        <pre class="programlisting">            db.put(null, key, data);  </pre>
        <p>
            Finally, we close our database before returning from the
            method.
        </p>
        <pre class="programlisting">        }
        if (db != null)
            db.close(true);
        return 0;
    }  </pre>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="wrfor_printstocks_c"></a>
                            
                            
                            <span>Method: RepQuoteWrforExampleGSG.printStocks()</span>    </h3>
            </div>
          </div>
        </div>
        <p>
               This function is unmodified from when we
               originally introduced it. For details on that function, see
               <a class="xref" href="simpleprogramlisting.html#printstocks_c" title="Method: SimpleTxn.printStocks()">
                            
                            
                            <span>Method: SimpleTxn.printStocks()</span>
                    </a>.
            </p>
      </div>
    </div>
    <div class="navfooter">
      <hr />
      <table width="100%" summary="Navigation footer">
        <tr>
          <td width="40%" align="left"><a accesskey="p" href="writeforwarding.html">Prev</a> </td>
          <td width="20%" align="center">
            <a accesskey="u" href="writeforwarding.html">Up</a>
          </td>
          <td width="40%" align="right"> <a accesskey="n" href="addfeatures.html">Next</a></td>
        </tr>
        <tr>
          <td width="40%" align="left" valign="top">Chapter 5. Configuring for Write Forwarding </td>
          <td width="20%" align="center">
            <a accesskey="h" href="index.html">Home</a>
          </td>
          <td width="40%" align="right" valign="top"> Chapter 6. Additional Features</td>
        </tr>
      </table>
    </div>
  </body>
</html>
